package pers.whj.net.local.client;

import pers.whj.net.local.exception.ErrorCodeEnum;
import pers.whj.net.local.exception.WHJException;
import pers.whj.net.local.task.FileTask;
import pers.whj.net.local.task.Task;
import pers.whj.net.local.task.FileInfoTask;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class ClientService {
    private static final int TIME_TO_END = 10 * 1000;
    private static final List<Task> TASKS;
    private static final int MAX_TASKS;
    private static final Object LOCK;
    private static Client sClient;
    private static Task sRunningTask;
    private static boolean sIsRunning;
    private static Timer sTimer;

    static {
        TASKS = new ArrayList<>();
        MAX_TASKS = Integer.MAX_VALUE;
        LOCK = new Object();
        sRunningTask = null;
        sIsRunning = false;
    }

    private static class TaskThread extends Thread {

        @Override
        public void run() {
            Task task = sRunningTask;
            if (task != null) {
                task.setStartTime(System.currentTimeMillis());
                switch (task.getType()) {
                    case FileTask.UPLOAD_FILE:
                    case FileTask.DOWNLOAD_FILE:
                    case FileInfoTask.GET_REMOTE_FILE_INFO:
                    case FileInfoTask.DELETE_REMOTE_FILE:
                    case FileInfoTask.RENAME_REMOTE_FILE:
                    case FileInfoTask.CREATE_REMOTE_FILE:
                        try {
                            task.setSuccess(sClient.executeTask(task));
                        } catch (IOException | WHJException e) {
                            task.setSuccess(false);
                            task.setException(e);
                        }
                        break;
                    case FileTask.UPLOAD_DIRECTORY:
                        if (task instanceof FileTask) {
                            FileTask fileTask = (FileTask) task;
                            List<FileTask> subTaskList = fileTask.getSubTaskList();
                            if (subTaskList != null) {
                                for (FileTask tmp : subTaskList) {
                                    tmp.setPriority(tmp.getPriority() + 1);
                                    try {
                                        addTask(tmp);
                                    } catch (WHJException e) {
                                        tmp.setSuccess(false);
                                        tmp.setException(e);
                                    }
                                }
                            }
                        }
                        break;
                    case FileTask.DOWNLOAD_DIRECTORY:
                        if (task instanceof FileTask) {
                            FileTask fileTask = (FileTask) task;
                            FileInfoTask preTask = new FileInfoTask(FileInfoTask.GET_REMOTE_FILE_INFO);
                            preTask.setRemoteFilePath(fileTask.getRemoteFilePath());
                            try {
                                preTask.setSuccess(sClient.executeTask(preTask));
                            } catch (IOException | WHJException e) {
                                preTask.setSuccess(false);
                                preTask.setException(e);
                            }
                            if (preTask.isSuccess()) {
                                try {
                                    fileTask.addDownloadSubTask(preTask.getFileInfo());
                                    List<FileTask> subTaskList = fileTask.getSubTaskList();
                                    if (subTaskList != null) {
                                        for (FileTask tmp : subTaskList) {
                                            tmp.setPriority(tmp.getPriority() + 1);
                                            try {
                                                addTask(tmp);
                                            } catch (WHJException e) {
                                                tmp.setSuccess(false);
                                                tmp.setException(e);
                                            }
                                        }
                                    }
                                } catch (WHJException e) {
                                    fileTask.setSuccess(false);
                                    fileTask.setException(e);
                                }
                            } else {
                                fileTask.setSuccess(false);
                                fileTask.setException(preTask.getException());
                            }
                        }
                        break;
                    default:
                        task.setSuccess(false);
                        break;
                }
                //check if this file is end
                if (task instanceof FileTask) {
                    FileTask curTask = (FileTask) task;
                    if (curTask.subTaskCounts() == 0) {
                        curTask.setEndTime(System.currentTimeMillis());
                        if (curTask.isLastSubTask()) {
                            curTask.getParentTask().onAllSubTaskEnd();
                        }
                    }
                } else {
                    task.setEndTime(System.currentTimeMillis());
                }
            }
            onTaskEnd();
        }
    }

    public static boolean start(String clientName, String defaultServerIP) throws WHJException {
        return start(new Client(clientName, defaultServerIP));
    }

    public static boolean start(Client client) throws WHJException {
        if (client != null && sClient == null) {
            sClient = client;
        }
        return start();
    }

    public static boolean start() throws WHJException {
        checkClient();
        if (!isServerConnected()) {
            if (sClient.createConnection()) {
                startTimer();
                return true;
            } else {
                return false;
            }
        }
        return true;
    }

    public static boolean end() {
        if (sIsRunning) {
            return false;
        }
        sClient.endConnection();
        endTimer();
        return true;
    }

    public static boolean setClient(Client client) {
        if (client != null && end()) {
            sClient = client;
            return true;
        }
        return false;
    }

    public static boolean isServerConnected() {
        return sClient != null && (sIsRunning || sClient.connectionCreated());
    }

    public static boolean addTask(Task task) throws WHJException {
        checkClient();
        if (!isServerConnected()) {
            throw new WHJException(ErrorCodeEnum.DISCONNECTION);
        }
        if (task != null) {
            synchronized (LOCK) {
                if (TASKS.size() < MAX_TASKS) {
                    int index = 0;
                    for (; index < TASKS.size(); index++) {
                        if (task.getPriority() > TASKS.get(index).getPriority()) {
                            break;
                        }
                    }
                    TASKS.add(index, task);
                    if (!sIsRunning) {
                        execute(TASKS.remove(0));
                    }
                    endTimer();
                    return true;
                }
            }
        }
        return false;
    }

    public static Task getRunningTask() {
        return sRunningTask;
    }

    public static boolean isRunning() {
        return sIsRunning;
    }

    private static void checkClient() throws WHJException {
        if (sClient == null) {
            throw new WHJException("客户端对象未初始化");
        }
    }

    private static void execute(Task task) {
        sIsRunning = true;
        sRunningTask = task;
        new TaskThread().start();
    }

    private static void onTaskEnd() {
        synchronized (LOCK) {
            if (TASKS.size() > 0) {
                execute(TASKS.remove(0));
            } else {
                sIsRunning = false;
                sRunningTask = null;
                startTimer();
            }
        }
    }

    private static void startTimer() {
        if (sTimer == null) {
            sTimer = new Timer();
            sTimer.schedule(generateTimerTask(), TIME_TO_END, 1000);
        }
    }

    private static void endTimer() {
        if (sTimer != null) {
            sTimer.cancel();
            sTimer = null;
        }
    }

    private static TimerTask generateTimerTask() {
        return new TimerTask() {
            @Override
            public void run() {
                end();
            }
        };
    }
}